#include <string.h>
#include <assert.h>
#include "cache.h"

using namespace comm::sockcommu;
extern CMemPool*	_memPool;


namespace comm
{
namespace sockcommu
{



CRawCache::CRawCache(CMemPool* mp, unsigned int default_size)
: _mp(mp)
{
	if (NULL == _mp)
		_mp = _memPool;
	
	this->_mem = NULL;
	this->_block_size = 0;
	this->_data_head = 0;
	this->_data_len = 0;

	if (default_size > 0)
	{
		_mem = (char*)_mp->allocate(default_size, _block_size);
		//assert(_mem);
		if (!_mem)
			throw(0);
	}
}

CRawCache::~CRawCache()
{
	this->release();
}

char* CRawCache::data()
{
    if (_data_len == 0)
        return NULL;

    assert(_data_head < _block_size);
    return _mem + _data_head;
}

unsigned int CRawCache::data_len()
{
	return _data_len;
}

unsigned int CRawCache::blocksize()
{
	return _block_size;
}

void CRawCache::append(const char* data, unsigned int data_len)
{
    //assert(data_len < 0x00010000);

    if (_mem == NULL)
    {
		_mem = (char*)_mp->allocate(data_len, _block_size);
        //assert(_mem);
        if(!_mem)
            throw(0);

        memcpy(_mem, data, data_len);

        _data_head = 0;
        _data_len = data_len;
        return;
    }
    
    //
    //	data_len < _block_size - _data_head - _data_len
    //	append directly
    //
    if (data_len + _data_head + _data_len <= _block_size)
    {
        memcpy(_mem + _data_head + _data_len, data, data_len);
        _data_len += data_len;
    }
    //
    //	_block_size-_data_len <= data_len
    //	reallocate new block. memmove, recycle
    //
    else if (data_len + _data_len > _block_size)
    {
        unsigned new_block_size = 0;
		char* mem = (char*)_mp->allocate(data_len + _data_len, new_block_size);
        //assert(mem);
        if(!mem)
        throw(0);

        memcpy(mem, _mem + _data_head, _data_len);
        memcpy(mem + _data_len, data, data_len);
		_mp->recycle(_mem, _block_size);

        _mem = mem;
        _block_size = new_block_size;
        _data_head = 0;
        _data_len += data_len;
    }
    //
    //	_block_size - _data_head - _data_len < data_len < _block_size-_datalen
    //	memmove data to block head, append new data
    //
    else
    //if ((data_len + _data_head + _data_len > _block_size) && (data_len + _data_len < _block_size))
    {
        memmove(_mem, _mem+_data_head, _data_len);
        memcpy(_mem+_data_len, data, data_len);

        _data_head = 0;
        _data_len += data_len;
    }
}
void CRawCache::skip(unsigned int length)
{
    //	empty data
    if (_mem == NULL)
        return;

    //	not enough data
    else if (length >= _data_len)
    {
		_mp->recycle(_mem, _block_size);
        _mem = NULL;
        _block_size = _data_head = _data_len = 0;
        _data_head = 0;
        _data_len = 0;
    }
    else
    {//	skip part of data
        _data_head += length;
        _data_len -= length;
    }
}

void CRawCache::extend(unsigned int size)
{
	if (_block_size >= size)
		return;

	if (NULL == _mem)
	{
		_mem = (char*)_mp->allocate(size, _block_size);
		//assert(_mem);
		if (!_mem)
			throw(0);

		_data_head = 0;
		_data_len = 0;
		return;
	}
	else
	{
		unsigned new_block_size = 0;
		char* mem = (char*)_mp->allocate(size, new_block_size);
		//assert(mem);
		if (!mem)
			throw(0);

		memcpy(mem, _mem + _data_head, _data_len);
		_mp->recycle(_mem, _block_size);

		_mem = mem;
		_block_size = new_block_size;
		_data_head = 0;

	}

}

void CRawCache::clear()
{
	_data_head = 0;
	_data_len = 0;

	if (NULL != _mem)
	{
		memset(_mem, 0, _block_size);
	}
}

void CRawCache::release()
{
	if (NULL != _mem)
	{
		_mp->recycle(_mem, _block_size);
		_mem = NULL;
	}
	_block_size = _data_head = _data_len = 0;
}

}
}


